﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.IO.Ports;
using System.Windows.Forms;
using System.Threading;
using System.IO;
using System.Security;

namespace SerialTool
{
    /// <summary>
    /// 声明串口类
    /// </summary>
    public class Comm
    {
        /// <summary>
        /// 在UI上更新接收到的数据的值
        /// </summary>
        /// <param name="readBuffer"></param>
        public delegate void EventHandle(byte[] readBuffer);
        /// <summary>
        /// 声明事件处理接收到的数据
        /// </summary>
        public event EventHandle DataReceived;

        /// <summary>
        /// 串口组件
        /// </summary>
        public SerialPort serialPort;

        /// <summary>
        /// 线程
        /// </summary>
        public Thread thread;

        /// <summary>
        /// 保持一直读取数据的标识符
        /// </summary>
        private volatile bool _keepReading;

        /// <summary>
        /// 取消文件发送的标识符，用来取消发送文件，TRUE取消发送，FALSE不取消发送
        /// </summary>


        /// <summary>
        /// 传输文件路径保存
        /// </summary>
        public string SendFilePath;

        
            

        /// <summary>
        /// 控制传输协议参数
        /// </summary>
        public ControlProtocal controlprotocal
        {
            set
            { 
                this.controlprotocal = controlprotocal;    
            }
            get
            {
                return this.controlprotocal;
            }
        }

        /// <summary>
        /// 构造函数
        /// </summary>
        public Comm()
        {
            serialPort = new SerialPort();
            thread = null;
            

            _keepReading = false;    
        }

         ~Comm()
        {
            if (null != thread)
            {
                if (true == thread.IsAlive)
                {
                    thread.Join();
                    thread = null;
                }
            }
        }

        /// <summary>
        /// 判断串口是否打开
        /// </summary>
        public bool IsOpen
        {
            get
            {
                return serialPort.IsOpen;
            }
        }

        /// <summary>
        /// 开始读取串口数据，开启线程读取数据
        /// </summary>
        public void StartReading()
        {
            if (!_keepReading)
            {
                //serialPort.DiscardInBuffer();
                _keepReading = true;
                thread = new Thread(new ThreadStart(ReadPort));
                thread.Start();
            }
        }
        
        /// <summary>
        /// 停止读取串口数据
        /// </summary>
        public void StopReading()
        {
            if (_keepReading)
            {                
                try
                {
                    _keepReading = false;
                    thread.Join();  //停止读串口数据线程
                }
                catch (System.Threading.ThreadStartException)
                {
                    MessageBox.Show("停止串口读取程序失败，ThreadStartException");
                }
                catch (System.Threading.ThreadInterruptedException)
                {
                    MessageBox.Show("停止串口读取程序失败，ThreadInterruptException");
                }
                finally 
                {
                    thread = null;
                }                           
            }
        }

        /// <summary>
        /// 在线程中读取到串口数据，并处理
        /// </summary>
        private void ReadPort()
        {
            while (_keepReading)
            {
                if (serialPort.IsOpen)
                {
                    //有串口数据可读，数据长度为count
                    int count = serialPort.BytesToRead;
                    if (count > 0)
                    {
                        //定义缓冲区来存取读到的数据
                        byte[] readBuffer = new byte[count];
                        try
                        {
                            Application.DoEvents();
                            //读取数据，然后将读取到的数据发送给DataReceived处理
                            serialPort.Read(readBuffer, 0, count);
                            if (DataReceived != null)
                                DataReceived(readBuffer);
                            Thread.Sleep(100);
                        }
                        catch (TimeoutException)
                        {
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 打开串口，并打开读取数据的线程
        /// </summary>
        public void Open()
        {
            Close();
            try
            {
                serialPort.Open();
            }
            catch (System.InvalidOperationException)
            {
                //对象的当前状态使该操作无效。 
                MessageBox.Show("InvalidOperationException：打开串口出错 1");
                return;
            }
            catch (System.ArgumentException)
            {
                MessageBox.Show("ArgumentException：打开串口出错2");
                return;
            }
            catch (System.IO.IOException)
            {
                //IO操作异常             
                MessageBox.Show("IOException：打开串口出错3");
                return;
            }
            catch (System.UnauthorizedAccessException)
            {
                //无访问权限              
                MessageBox.Show("UnauthorizedAccessException：端口拒绝访问");
                return;
            }
            finally
            {
                if (serialPort.IsOpen)
                {
                    StartReading();
                }
                else
                {
                    MessageBox.Show("串口打开失败！");
                }
            }

            
        }

        /// <summary>
        /// 关闭串口，在关闭串口之前要先关闭读线程
        /// </summary>
        public void Close()
        {
            StopReading();
            serialPort.Close();
        }

        /// <summary>
        /// 向串口中写入count个数据
        /// </summary>
        /// <param name="send"></param>
        /// <param name="offSet"></param>
        /// <param name="count"></param>
        public void WritePort(byte[] send, int offSet, int count)
        {
            if (IsOpen)
            {
                serialPort.Write(send, offSet, count);
            }
        }

        /// <summary>
        /// 向串口中发送一个字节的数据
        /// </summary>
        /// <param name="data"></param>
        public void Send_Byte(byte data)
        {
            if (IsOpen)
            {                              
                byte[] tmp = new byte[1];
                tmp[0] = data;
                serialPort.Write(tmp, 0, 1);
            }
        }	

        /// <summary>
        /// 从串口中读取到一个字节的数据
        /// </summary>
        /// <returns></returns>
        public byte Receive_Byte(byte[] data, int waittime)
        {
            byte tmp = 0;
            if (IsOpen)
            {
                try
                {
                    serialPort.ReadTimeout = waittime;
                    tmp = (byte)serialPort.ReadByte();
                }
                catch (System.InvalidOperationException)
                {
                    return 1;
                }
                catch (System.SystemException)
                {
                    return 1;
                }
                finally
                {
                    data[0] = tmp;
                }
                return 0;
            }
            else
            {
                return 1;
            }
        }

        public void ClearReadBuffer()
        {
            if(IsOpen)
            {
                serialPort.DiscardOutBuffer();
            }
        }

        /// <summary>
        /// Xmodem协议传输文件
        /// </summary>
        /// <returns></returns>
        private int XmodemSendFile()
        { 
            if (SendFilePath.Length == 0)
            {
                return -1;
            }            
           
            //以二进制的方式打开文件
            BinaryReader sr = new BinaryReader(File.Open(SendFilePath, FileMode.Open));
           
            byte[] dataPackage = new byte[132];     //定义132字节的数据包，3byte 头字节+128byte 数据字节+2byte 校验
            Array.Clear(dataPackage, 0, 132);       //清空数据包
            byte[] signal = new byte[132];          //信号
            byte packageNumber = 0;                 //包号
            int i;
            int ret = 0;

           

            Comm sp = new Comm(); 
      
            sp.StopReading();                       //停止线程接收数据


            signal[0] = 0x01;                       //起始信号SOH
            sp.WritePort(signal, 0, 132);           //发送起始信号SOH
            signal[0] = (byte)sp.serialPort.ReadByte();

            if (signal[0] == 0x15)              //第一次收到NAK信号
            {
                //this.rtbReceive.AppendText("开始传输文件\n");

                while (sr.Read(dataPackage, 3, 128) != 0) //文件没有结束，则从文件中读取128字节数据，并打包发送
                {
                    dataPackage[0] = 0x01;                  //包头SOH
                    dataPackage[1] = packageNumber;         //包号
                    dataPackage[2] = (byte)~packageNumber;  //包号取反
                    dataPackage[131] = 0;                   //清零校验码
                    for (i = 3; i < 131; i++)
                    {
                        dataPackage[131] += dataPackage[i]; //计算检验码
                    }

                sendPackage:
                    sp.serialPort.DiscardOutBuffer();           //清空串口输出缓冲区
                    sp.serialPort.Write(dataPackage, 0, 132);   //发送128byte数据包

                    //this.rtbReceive.AppendText("发送数据包" + packageNumber.ToString() + "\n");
                    signal[0] = (byte)sp.serialPort.ReadByte();

                    if (signal[0] == 0x06) //收到ACK信号
                    {
                        packageNumber++;  //包号加1
                    }
                    else if (signal[0] == 0x15) //收到NAK信号
                    {
                        //this.rtbReceive.AppendText("数据包" + packageNumber.ToString() + "发送失败，重发\n");
                        goto sendPackage; //重发
                    }
                    else
                    {
                        //this.rtbReceive.AppendText("文件传输错误");
                        Array.Clear(dataPackage, 0, 132); //清空数据包
                    }

                    //睡眠等待一下下位机处理数据包，然后再发送下一包数据
                    //处理进度条，并等待其他按钮处理，是否取消发送数据包
                    if (new MainDialog().DownloadFileButton.Text == "取消烧写")
                    {
                        sr.Close();
                        return -1;
                    }
                }

                signal[0] = 0x04;                       //EOT信号
                sp.serialPort.Write(signal, 0, 132);    //发送EOT信号
                if (sp.serialPort.ReadByte() == 0x06)   //收到ACK信号
                {
                    //this.rtbReceive.AppendText("文件传输成功\n");
                    //发送成功，处理进度条
                    ret = 0;
                }
                else
                {
                    //this.rtbReceive.AppendText("文件传输错误");
                    ret = 1;
                }
                
            }
            else
            {
                //this.rtbReceive.AppendText("文件传输错误");
                
                ret = -1;
                
            }

            sp.StartReading();                      //开始线程接收数据
            return ret;                             //发送成功

        }    
    }
}
